﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Management;
using System.Text;

namespace Hadron.License
{
    enum LicenseType
    {
        Demo,
        Full,
        None
    }

    class LicenseManager
    {
        private static LicenseManager s_licenseManager = new LicenseManager();
        private static byte[] s_arrDefaultMachineKey = ConvertToByteArray("DEFAULT");

        private String m_strApplicationName;
        private LicenseType m_licenseType;

        private static long ConvertToNumber(String p_strKeySegment)
        {
            long lValue = 0;
            foreach (char ch in p_strKeySegment)
                lValue = lValue * 26 + (ch - 'A');
            return lValue;
        }

        private static byte[] ConvertToByteArray(String p_strData)
        {
            byte[] arrData = new byte[p_strData.Length];
            for (int nIndex = 0; nIndex < p_strData.Length; nIndex++)
                arrData[nIndex] = (byte)p_strData[nIndex];
            return arrData;
        }

        private static String ConvertToString(byte[] p_arrData)
        {
            String strData = "";
            foreach (char ch in p_arrData)
                strData += ch;
            return strData;
        }

        private LicenseManager()
        {
            m_strApplicationName = "Default";
            m_licenseType = LicenseType.None;
        }

        private byte[] GetMachineKey()
        {
            ManagementClass managementClass = new ManagementClass("Win32_NetworkAdapterConfiguration");

            ManagementObjectCollection managementObjectCollection
                = managementClass.GetInstances();

            if (managementObjectCollection.Count == 0)
                return s_arrDefaultMachineKey;

            foreach (ManagementObject managementObject in managementObjectCollection)
            {
                object objProperty = managementObject["MacAddress"];
                if (objProperty == null)
                    continue;
                String strMacAddress = objProperty.ToString();

                return ConvertToByteArray(strMacAddress);
            }

            return s_arrDefaultMachineKey;
        }

        private byte[] EncryptMessage(byte[] p_arrKey, byte[] p_arrMessage)
        {
            byte[] arrCipher = new byte[p_arrMessage.Length];
            for (int nIndex = 0; nIndex < p_arrMessage.Length; nIndex++)
                arrCipher[nIndex] = (byte) (p_arrMessage[nIndex] ^ p_arrKey[nIndex % p_arrKey.Length]);
            return arrCipher;
        }

        private byte[] DecryptMessage(byte[] p_arrKey, byte[] p_arrCipher)
        {
            // currently symmetric
            return EncryptMessage(p_arrKey, p_arrCipher);
        }

        private void StoreLicense(String p_strLicenseKey)
        {
            byte[] arrMachineKey = GetMachineKey();
            String strData = m_strApplicationName + "_" + p_strLicenseKey + "_" + ConvertToString(arrMachineKey);
            byte[] arrData = ConvertToByteArray(strData);
            byte[] arrCipher = EncryptMessage(arrMachineKey, arrData);

            FileStream fileStream = File.OpenWrite(m_strApplicationName + ".license");
            fileStream.Write(arrCipher, 0, arrCipher.Length);
            fileStream.Close();
        }

        private void LoadLicense()
        {
            m_licenseType = LicenseType.Demo;

            try
            {
                byte[] arrBuffer = new byte[1024];
                FileStream fileStream = File.Open(m_strApplicationName + ".license", FileMode.Open);
                int nBufferSize = fileStream.Read(arrBuffer, 0, arrBuffer.Length);
                fileStream.Close();

                byte[] arrCipher = new byte[nBufferSize];
                for (int nIndex = 0; nIndex < nBufferSize; nIndex++)
                    arrCipher[nIndex] = arrBuffer[nIndex];

                byte[] arrMachineKey = GetMachineKey();
                byte[] arrData = DecryptMessage(arrMachineKey, arrCipher);

                String strData = ConvertToString(arrData);

                String[] strTokens = strData.Split('_');
                if (strTokens.Length != 3)
                    return;

                if (strTokens[0] != m_strApplicationName)
                    return;

                if (!IsValidKey(strTokens[1]))
                    return;

                if (strTokens[2] != ConvertToString(arrMachineKey))
                    return;

                m_licenseType = LicenseType.Full;
            }
            catch (Exception)
            {
                // treat as unlicensed
            }
        }

        public static LicenseManager Instance
        {
            get { return s_licenseManager; }
        }

        public bool IsValidKeyFormat(String p_strKey)
        {
            if (p_strKey.Length != 19)
                return false;

            String[] strSegments = p_strKey.Split('-');
            if (strSegments.Length != 4)
                return false;

            foreach (String strSegment in strSegments)
            {
                if (strSegment.Length != 4)
                    return false;
                foreach (char ch in strSegment)
                    if (!Char.IsUpper(ch))
                        return false;
            }

            return true;
        }

        public bool IsValidKey(String p_strKey)
        {
            if (!IsValidKeyFormat(p_strKey))
                return false;
            
            p_strKey = p_strKey.Replace("-", "");

            if (ConvertToNumber(p_strKey.Substring(0, 4)) % 11 != 0)
                return false;

            if (ConvertToNumber(p_strKey.Substring(4, 4)) % 17 != 0)
                return false;

            if (ConvertToNumber(p_strKey.Substring(8, 4)) % 13 != 0)
                return false;

            if (ConvertToNumber(p_strKey.Substring(12, 4)) % 29 != 0)
                return false;

            return true;
        }

        public void ValidateLicense()
        {
            LoadLicense();

            if (m_licenseType == LicenseType.Full)
                return;

            LicenseForm licenseForm = new LicenseForm();
            if (licenseForm.ShowDialog() == System.Windows.Forms.DialogResult.Cancel)
            {
                m_licenseType = LicenseType.None;
                return;
            }

            m_licenseType =  licenseForm.ValidKey ? LicenseType.Full : LicenseType.Demo;

            if (m_licenseType == LicenseType.Full)
                StoreLicense(licenseForm.LicenseKey);
        }

        public String ApplicationName
        {
            get { return m_strApplicationName; }
            set { m_strApplicationName = value; }
        }

        public LicenseType LicenseType
        {
            get { return m_licenseType; }
        }
    }
}
